<template>
    <div :class="[formItem ? 'c-f-grow-1' : '']">
        <input
            type="file"
            multiple
            ref="input"
            @change="inputFile"
            class="c-hide"
            :accept="accept"
        />

        <a-button
            v-show="canAdd && !myDisabled"
            :color="myColor"
            :size="mySize"
            :sharp="mySharp"
            :disabled="myDisabled"
            :class="[{ 'c-m-b-m': items.length > 0 }]"
            @click="openFileSelector"
        >
            <span class="fa fa-plus"></span>
            <span> 选择图片</span>
        </a-button>

        <a-table
            :headerList="headerList"
            :dataList="items"
            :selectable="false"
            :isInner="true"
            v-if="items.length > 0"
            ref="table"
        >
            <template v-slot:data-thumbnail="{ data }">
                <a-fitimg
                    v-if="data.src"
                    :src="data.src"
                    @info="setInfo(data.id, $event)"
                    style="width: 100px; height: 100px"
                >
                </a-fitimg>
            </template>

            <template v-slot:data-size="{ data }">
                {{ getSizeStr(data.size) }}
            </template>

            <template v-slot:data-status="{ data }">
                {{ statusList[data.status] }}
            </template>

            <template v-slot:data-operations="{ data }">
                <a-control-group>
                    <a-button
                        color="error"
                        :disabled="myDisabled"
                        @click="removeItem(data.id)"
                        >删除</a-button
                    >
                    <a-button
                        v-if="data.status == 2"
                        color="default"
                        @click="upload(data.id)"
                        >重试</a-button
                    >
                    <a-button
                        v-if="data.status == 1"
                        color="primary"
                        :disabled="myDisabled"
                        :square="true"
                        @click="up(data.id)"
                    >
                        <span class="fa fa-long-arrow-up"></span>
                    </a-button>
                    <a-button
                        v-if="data.status == 1"
                        color="secondary"
                        :disabled="myDisabled"
                        :square="true"
                        @click="down(data.id)"
                    >
                        <span class="fa fa-long-arrow-down"></span>
                    </a-button>
                </a-control-group>
            </template>
        </a-table>
    </div>
</template>

<script>
import AButton from './AButton.vue'
import AControlGroup from './AControlGroup.vue'
import AFitimg from './AFitimg.vue'
import ATable from './ATable.vue'
import inputMixin from "../common/inputMixin"
import utils from '../common/utils'

export default {
    components: { AButton, AControlGroup, AFitimg, ATable },
    mixins: [inputMixin],
    props: {
        value: {
            type: Array,
            default: []
        },
        getSrcFunc: {
            type: Function,
            default: id => `https://abc.xyz/file/${id}`
        },
        accept: {
            type: String,
            default: 'image/jpeg, image/bmp, image/png, image/gif'
        },
        uploadFunc: {
            type: Function,
            default: file => 'fakeimg.ext'
        },
        minWidth: {
            type: Number,
        },
        maxWidth: {
            type: Number,
        },
        minHeight: {
            type: Number,
        },
        maxHeight: {
            type: Number,
        },
        minSize: {
            type: Number,
        },
        maxSize: {
            type: Number,
        },
        validateImgFunc: {
            type: Function,
            default: (width, height, size) => true
        }
    },
    data() {
        return {
            uploadedItems: [],
            uploadingItems: [],
            headerList: [
                { name: 'thumbnail', text: '缩略图', width: 20 },
                { name: 'width', text: '宽', width: 20 },
                { name: 'height', text: '高', width: 20 },
                { name: 'size', text: '大小', width: 20 },
                { name: 'status', text: '状态', width: 20 },
                { name: 'operations', text: '操作', width: 30 },
            ],
            statusList: ['上传中', '已上传', '上传失败', '不符合要求']
        }
    },
    computed: {
        items() {
            return [...this.uploadedItems, ...this.uploadingItems]
        },
        canAdd() {
            let rule = this.rules.find(a => a.name == 'maxLength')
            if (!rule) {
                return true
            }
            return this.items.length <= rule.value
        },
        /**
         * 由于value已经在mixin中被watch，这里要继续watch value的话，就会覆盖mixin的watch了，所以换一个值名称
         */
        valueCloneForWatch() {
            return this.value
        },
    },
    mounted() {
        this.refreshUpdatedItems()
    },
    watch: {
        valueCloneForWatch() {
            this.refreshUpdatedItems()
        }
    },
    methods: {
        input(val) {
            this.$emit('update:value', val)
        },
        openFileSelector() {
            this.$refs.input.click()
        },
        inputFile: function () {
            this.$refs.input.files.forEach(file => {
                if (!this.uploadingItems.some(a => a.file.name == file.name)) {
                    let uploadingItem = {
                        id: `uploading_${file.name}`,
                        width: 0,
                        height: 0,
                        size: file.size,
                        status: 0,
                        file,
                        src: null,
                        valid: false
                    }
                    this.uploadingItems = [...this.uploadingItems, uploadingItem]
                    this.resolve(uploadingItem.id)
                }
            })
            this.$refs.input.value = ''
        },
        async resolve(itemId) {
            let item = this.getItemById(itemId)
            if (!item) {
                return
            }
            let dataUrl = await utils.getDataUrlFromFile(item.file)
            item.src = dataUrl
        },
        async upload(itemId) {
            let item = this.getItemById(itemId)
            if (!item) {
                return
            }
            if (item.valid) {
                item.status = 0
            } else {
                item.status = 3
                return
            }
            try {
                let id = await this.uploadFunc(item.file)
                this.removeItem(itemId)
                let valueSet = new Set(this.value)
                valueSet.add(id)
                this.input(Array.from(valueSet))
            } catch (error) {
                console.error(error)
                item.status = 2
            }
        },
        removeItem(itemId) {
            let item = this.getItemById(itemId)
            if (!item) {
                return
            }
            if (item.file) {
                this.removeUploadingItem(itemId)
            } else {
                this.removeUploadedItem(itemId)
            }
        },
        removeUploadedItem(itemId) {
            this.input(this.value.filter(a => `uploaded_${a}` != itemId))
        },
        removeUploadingItem(itemId) {
            this.uploadingItems = this.uploadingItems.filter(a => a.id != itemId)
        },
        refreshUpdatedItems() {
            this.uploadedItems = this.value.map(a => {
                return {
                    id: `uploaded_${a}`,
                    width: 0,
                    height: 0,
                    size: 0,
                    status: 1,
                    src: this.getSrcFunc(a),
                    value: a
                }
            })
        },
        setInfo(itemId, info) {
            let item = this.getItemById(itemId)
            if (!item) {
                return
            }
            item.width = info.width
            item.height = info.height

            if (item.file) {
                item.valid = this.isItemValid(item)
                this.upload(itemId)
            }
        },
        getItemById(id) {
            return this.items.find(a => a.id == id)
        },
        getSizeStr(size) {
            if (!size) {
                return '未知'
            }
            return utils.getStorageSizeStr(size)
        },
        isItemValid(item) {
            let { width, height, size } = item
            if (!width || !height || !size) {
                return false
            }
            return (this.minWidth == null || width >= this.minWidth)
                && (this.maxWidth == null || width <= this.maxWidth)
                && (this.minHeight == null || height >= this.minHeight)
                && (this.maxHeight == null || height <= this.minHeight)
                && (this.minSize == null || size >= this.minSize)
                && (this.maxSize == null || size <= this.minSize)
                && this.validateImgFunc(width, height, size)
        },
        up(itemId) {
            let tempItems = [...this.uploadedItems]
            let index = tempItems.findIndex(a => a.id == itemId)
            if (index >= 1) {
                [tempItems[index], tempItems[index - 1]] = [tempItems[index - 1], tempItems[index]]
                this.input(tempItems.map(a => a.value))
            }
        },
        down(itemId) {
            let tempItems = [...this.uploadedItems]
            let index = tempItems.findIndex(a => a.id == itemId)
            if (index <= tempItems.length - 2) {
                [tempItems[index], tempItems[index + 1]] = [tempItems[index + 1], tempItems[index]]
                this.input(tempItems.map(a => a.value))
            }
        },
    }
}
</script>